home *** CD-ROM | disk | FTP | other *** search
/ AOL File Library: 2,801 to 2,900 / aol-file-protocol-4400-2801-to-2900.zip / AOLDLs / C++ Files Library / HyperCuber Source / HyperCuber 2.0 Source.sit / HyperCuber 2.0 Source / CEnhancedWindow.cp < prev    next >
Text File  |  1994-04-29  |  19KB  |  498 lines

  1. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2. //| CEnhancedWindow.cp
  3. //|
  4. //| This file contains the implementation of an enhanced window.  This window
  5. //| is more aware of multiple screens that a normal window, and can place
  6. //| and zoom itself intelligently depending on the screen setup.
  7. //|___________________________________________________________________________
  8.  
  9. #include "CEnhancedWindow.h"
  10. #include <limits.h>
  11.  
  12.  
  13.  
  14. //=============================== Procedure Prototypes ===============================\\
  15.  
  16. pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
  17.                                     long userData);
  18. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
  19.                             short idealOnScreenStartPoint, short idealOnScreenEndPoint,
  20.                             short screenEdge1, short screenEdge2);
  21.  
  22.  
  23. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24. //| CEnhancedWindow::IEnhancedWindow
  25. //|
  26. //| Purpose: Initialize the window
  27. //|
  28. //| Parameters: passed to superclass
  29. //|______________________________________________________________
  30.  
  31. void CEnhancedWindow::IEnhancedWindow(short WINDid, Boolean aFloating,
  32.                         CDesktop *anEnclosure, CDirector *aSupervisor)
  33. {
  34.  
  35.     inherited::IWindow(WINDid, aFloating, anEnclosure, aSupervisor);
  36.  
  37.     title_bar_height = 18;
  38.  
  39. }    //==== CEnhancedWindow::IEnhancedWindow() ====\\
  40.  
  41.  
  42.  
  43. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  44. //| CEnhancedWindow::Zoom
  45. //|
  46. //| Purpose: Zoom the window.  This code is from Develop magazine, issue 17.
  47. //|
  48. //| Parameters: passed to superclass
  49. //|__________________________________________________________________________
  50.  
  51.         struct ZoomData {
  52.             GDHandle        screenWithLargestPartOfWindow;
  53.             unsigned long    largestArea;
  54.             Rect            windowBounds;
  55.         };
  56.         typedef struct ZoomData ZoomData, *ZoomDataPtr;
  57.         
  58.         enum {
  59.             kNudgeSlop    =    4,
  60.             kIconSpace    =    64
  61.         };
  62.  
  63. void CEnhancedWindow::Zoom(short zoomState)
  64. {
  65.  
  66.     ZoomData    zoomData;
  67.     Rect        newStandardRect;
  68.     Rect        scratchRect;
  69.     Rect        screenRect;
  70.     Rect        portRect;
  71.     Rect        contentRegionBoundingBox;
  72.     Rect        structureRegionBoundingBox;
  73.     Rect        deviceLoopRect;
  74.     GrafPtr        currentPort;
  75.     RgnHandle    scratchRegion;
  76.     RgnHandle    contentRegion;
  77.     RgnHandle    structureRegion;
  78.     GDHandle    mainDevice;
  79.     short        horizontalAmountOffScreen;
  80.     short        verticalAmountOffScreen;
  81.     short        windowFrameTopSize;
  82.     short        windowFrameLeftSize;
  83.     short        windowFrameRightSize;
  84.     short        windowFrameBottomSize;
  85.     
  86.  
  87.     GetPort(¤tPort);
  88.     Prepare();
  89.     contentRegion = ((CWindowPeek) GetMacPort())->contRgn;
  90.     structureRegion = ((CWindowPeek) GetMacPort())->strucRgn;
  91.     portRect = ((CWindowPeek) GetMacPort())->port.portRect;
  92.     contentRegionBoundingBox = (**contentRegion).rgnBBox;
  93.     structureRegionBoundingBox = (**structureRegion).rgnBBox;
  94.     
  95.     // Determine the size of the window frame
  96.     windowFrameTopSize = contentRegionBoundingBox.top - 
  97.                                     structureRegionBoundingBox.top;
  98.     windowFrameLeftSize = contentRegionBoundingBox.left - 
  99.                                     structureRegionBoundingBox.left;
  100.     windowFrameRightSize = structureRegionBoundingBox.right - 
  101.                                     contentRegionBoundingBox.right;
  102.     windowFrameBottomSize = structureRegionBoundingBox.bottom - 
  103.                                     contentRegionBoundingBox.bottom;
  104.                                     
  105.     // If the window is being zoomed into the standard state, calculate the best size
  106.     // to display the window╒s information.
  107.     mainDevice = GetMainDevice();
  108.     if (zoomState == inZoomOut) {
  109.         zoomData.screenWithLargestPartOfWindow = mainDevice;
  110.         zoomData.largestArea = 0;
  111.     
  112.         // Usually, we would use the content region╒s bounding box to determine the monitor
  113.         // with largest portion of the window╒s area. However, if the entire content region
  114.         // of the window is not on any screen, the structure region should be used instead.
  115.         scratchRegion = NewRgn();
  116.         SectRgn(GetGrayRgn(), contentRegion, scratchRegion);
  117.         if (EmptyRgn(scratchRegion))
  118.             zoomData.windowBounds = structureRegionBoundingBox;
  119.         else
  120.             zoomData.windowBounds = contentRegionBoundingBox;
  121.     
  122.         // Use DeviceLoop to walk through all the active screens to find the one with the
  123.         // largest portion of the zoomed window
  124.         deviceLoopRect = zoomData.windowBounds;
  125.         GlobalToLocal((Point *)&deviceLoopRect);
  126.         GlobalToLocal((Point *)&deviceLoopRect.bottom);
  127.         RectRgn(scratchRegion, &deviceLoopRect);
  128.         DeviceLoop(scratchRegion, &CalcWindowAreaOnScreen, (long) &zoomData,
  129.                     (DeviceLoopFlags) singleDevices);
  130.         DisposeRgn(scratchRegion);
  131.         screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
  132.         
  133.         // If the monitor being zoomed to is the main monitor, change the top of the
  134.         // useable screen area to avoid putting the title bar underneath the menubar.
  135.         if (zoomData.screenWithLargestPartOfWindow == mainDevice)
  136.             screenRect.top += GetMBarHeight();
  137.             
  138.         // Go figure out the perfect size for the window as if we had an infinitely large
  139.         // screen
  140.         FindIdealSize(&newStandardRect);
  141.         
  142.         // Anchor the new rectangle at the window╒s current top left corner
  143.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  144.         OffsetRect(&newStandardRect, contentRegionBoundingBox.left,
  145.                     contentRegionBoundingBox.top);
  146.         
  147.         // newStandardRect is the ideal size for the content area. The window frame
  148.         // needs to be accounted for when we see if the window needs to be moved,
  149.         // or resized, so add in the dimensions of the window frame.
  150.         newStandardRect.top -= windowFrameTopSize;
  151.         newStandardRect.left -= windowFrameLeftSize;
  152.         newStandardRect.right += windowFrameRightSize;
  153.         newStandardRect.bottom += windowFrameBottomSize;
  154.         
  155.         // If the new rectangle falls off the edge of the screen, nudge it so that it╒s just
  156.         // on the screen. CalculateOffsetAmount determines how much of the window is offscreen.
  157.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  158.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  159.             horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
  160.                                                                newStandardRect.right,
  161.                                                                scratchRect.left,
  162.                                                                scratchRect.right,
  163.                                                                screenRect.left,
  164.                                                                screenRect.right);
  165.             verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
  166.                                                             newStandardRect.bottom,
  167.                                                             scratchRect.top,
  168.                                                             scratchRect.bottom,
  169.                                                             screenRect.top,
  170.                                                             screenRect.bottom);
  171.             OffsetRect(&newStandardRect, horizontalAmountOffScreen,
  172.                         verticalAmountOffScreen);
  173.         }
  174.     
  175.         // If we╒re still falling off the edge of the screen, that means that the perfect
  176.         // size is larger than the screen, so we need to shrink down the standard size
  177.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  178.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  179.  
  180.         // First shrink the width of the window. If the window is wider than the screen
  181.         // it is zooming to, we can just pin the standard rectangle to the edges of the
  182.         // screen, leaving some slop. If the window is narrower than the screen, we know
  183.         // we just nudged it into position, so nothing needs to be done.
  184.             if ((newStandardRect.right - newStandardRect.left) >
  185.                 (screenRect.right - screenRect.left)) {
  186.                 newStandardRect.left = screenRect.left + kNudgeSlop;
  187.                 newStandardRect.right = screenRect.right - kNudgeSlop;
  188.  
  189.                 if ((zoomData.screenWithLargestPartOfWindow == mainDevice) &&
  190.                     (newStandardRect.right > (screenRect.right - kIconSpace)))
  191.                     newStandardRect.right = screenRect.right - kIconSpace;
  192.             }
  193.  
  194.             // Move in the top. Like the width of the window, nothing needs to be done unless
  195.             // the window is taller than the height of the screen.
  196.             if ((newStandardRect.bottom - newStandardRect.top) >
  197.                 (screenRect.bottom - screenRect.top)) {
  198.                 newStandardRect.top = screenRect.top + kNudgeSlop;
  199.                 newStandardRect.bottom = screenRect.bottom - kNudgeSlop;
  200.             }
  201.         }
  202.  
  203.         // We╒ve got the best possible window position. Remove the
  204.         // frame, slam it into the WStateData record and let ZoomWindow
  205.         // take care of the rest.
  206.         newStandardRect.top += windowFrameTopSize;
  207.         newStandardRect.left += windowFrameLeftSize;
  208.         newStandardRect.right -= windowFrameRightSize;
  209.         newStandardRect.bottom -= windowFrameBottomSize;
  210.         (**((WStateDataHandle) ((CWindowPeek) GetMacPort())->dataHandle)).stdState = newStandardRect;
  211.         //  the above line sets the standard state
  212.     }
  213.     else
  214.         newStandardRect = (**((WStateDataHandle) ((CWindowPeek) GetMacPort())->dataHandle)).userState;
  215.         //  the above line puts the user state in newStandardRect
  216.         
  217.     // If the window is still anchored at the current location, then just resize it
  218.     if ((newStandardRect.left == contentRegionBoundingBox.left) &&
  219.         (newStandardRect.top == contentRegionBoundingBox.top)) {
  220.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  221.         ChangeSize(newStandardRect.right, newStandardRect.bottom);
  222.         
  223.     }
  224.     else {
  225.         scratchRegion = NewRgn();
  226.         GetClip(scratchRegion);
  227.         ClipRect(&portRect);
  228.         EraseRect(&portRect);
  229.         inherited::Zoom(zoomState);                //  Zoom, using superclass
  230.         SetClip(scratchRegion);
  231.         DisposeRgn(scratchRegion);
  232.     }
  233.     
  234.     SetPort(currentPort);
  235.  
  236. }    //==== CEnhancedWindow:: Zoom() ====\\
  237.  
  238.  
  239.  
  240. pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
  241. {
  242.  
  243.     ZoomDataPtr    zoomData = (ZoomDataPtr) userData;
  244.     long        windowAreaOnScreen;
  245.     Rect        windowPortionOnScreen;
  246.     
  247.     // Find the rectangle that encloses the intersection of the window and this screen.
  248.     SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
  249.     
  250.     // Offset the rectangle so that it╒s right and bottom are also it╒s width and height.
  251.     OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
  252.     
  253.     // Calculate the area of the portion of the window that╒s on this screen.
  254.     windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
  255.     
  256.     // If this is the largest portion of the window that has been encountered so far,
  257.     // remember this screen as the potential screen to zoom to.
  258.     if (windowAreaOnScreen > zoomData->largestArea) {
  259.         zoomData->largestArea = windowAreaOnScreen;
  260.         zoomData->screenWithLargestPartOfWindow = targetDevice;
  261.     }
  262. }
  263.  
  264. // Figure out how much we need to move the window to get it entirely on the monitor.  If
  265. // the window wouldn╒t fit completely on the monitor anyway, don╒t move it at all; we╒ll
  266. // make it fit later on.
  267.  
  268. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
  269.                             short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
  270. {
  271.     short    offsetAmount;
  272.  
  273.     // First check to see if the window fits on the screen in this dimension.
  274.     if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
  275.         offsetAmount = 0;
  276.     else {
  277.     
  278.         // Find out how much of the window lies off this screen by subtracting the amount of the window
  279.         // that is on the screen from the size of the entire window in this dimension. If the window
  280.         // is completely offscreen, the offset amount is going to be the distance from the ideal
  281.         // starting point to the first edge of the screen.
  282.         if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
  283.             // See if the window is lying to the left or above the screen
  284.             if (idealEndPoint < screenEdge1)
  285.                 offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
  286.             else
  287.             // Otherwise, it╒s below or to the right of the screen
  288.                 offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
  289.         }
  290.         else {
  291.             // Window is already partially or completely on the screen
  292.             offsetAmount = (idealEndPoint - idealStartPoint) -
  293.                             (idealOnScreenEndPoint - idealOnScreenStartPoint);
  294.     
  295.             // If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
  296.             if (offsetAmount != 0)
  297.                 offsetAmount += kNudgeSlop;
  298.             
  299.             // Check to see which side of the screen the window was falling off of, so that it can be
  300.             // nudged in the opposite direction.
  301.             if (idealEndPoint > screenEdge2)
  302.                 offsetAmount = -offsetAmount;
  303.         }
  304.     }
  305.     
  306.     return offsetAmount;
  307. }
  308.  
  309.  
  310.  
  311. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  312. //| CEnhancedWindow::FindIdealSize
  313. //|
  314. //| Purpose: Compute ideal size for this window.overridden
  315. //|
  316. //| Parameters: rect: receives the ideal window size
  317. //|__________________________________________________________________________
  318.  
  319. void CEnhancedWindow::FindIdealSize(Rect *rect)
  320. {
  321.  
  322.     rect->left = 0;                    //  Default makes a square 500 by 500 window
  323.     rect->right = 500;
  324.     rect->top = 0;
  325.     rect->bottom = 500;
  326.  
  327. }    //==== CEnhancedWindow::FindIdealSize() ====\\
  328.  
  329.  
  330.  
  331. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  332. //| CEnhancedWindow::MakeFullScreen
  333. //|
  334. //| Purpose: Resizes the window to fill the screen
  335. //|
  336. //| Parameters: none
  337. //|___________________________________________________________________________
  338.  
  339. void CEnhancedWindow::MakeFullScreen(void)
  340. {
  341.     
  342.     Move(0, 0);                                            //  Move the window to the very top left.  Note that
  343.                                                         //   this moved the structure offscreen so only the
  344.                                                         //   contents is visible, and that even the top of
  345.                                                         //   the contents will be obscured unless the menu bar
  346.                                                         //   is hidden
  347.     
  348.     Rect main_monitor_bounds;
  349.     GDHandle graphics_device;
  350.     graphics_device = GetDeviceList ();                    //  Get the first (main) device in the list
  351.     main_monitor_bounds = (*graphics_device)->gdRect;    //  Get the main device bounds rect
  352.  
  353.     ChangeSize(main_monitor_bounds.right -                //  Resize the window to fill the screen
  354.                     main_monitor_bounds.left,
  355.                 main_monitor_bounds.bottom -
  356.                     main_monitor_bounds.top);
  357.  
  358. }    //==== CEnhancedWindow::MakeFullScreen() ====\\
  359.  
  360.  
  361.  
  362. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  363. //| CEnhancedWindow::Place
  364. //|
  365. //| Purpose: Place the window according to bounds.
  366. //|
  367. //| Parameters: bounds: the desired position of the window
  368. //|__________________________________________________________________________
  369.  
  370. void CEnhancedWindow::Place(Rect *bounds)
  371. {
  372.     
  373.     Move(bounds->left, bounds->top);                //  Move the window
  374.     ChangeSize(bounds->right - bounds->left,        //  Resize the window
  375.                 bounds->bottom - bounds->top);
  376.  
  377. }    //==== CEnhancedWindow::Place() ====\\
  378.  
  379.  
  380.  
  381. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  382. //| CEnhancedWindow::GetRect
  383. //|
  384. //| Purpose: Get the size and position of a window
  385. //|
  386. //| Parameters: window_rect: receives the current window position
  387. //|___________________________________________________________________________
  388.  
  389. void CEnhancedWindow::GetRect(Rect *window_rect)
  390. {
  391.  
  392.     Point upper_left;
  393.     upper_left.h = upper_left.v = 0;                            //  Find upper left corner
  394.     Prepare();
  395.     LocalToGlobal(&upper_left);
  396.  
  397.     LongRect window_interior;                                    //  Find height and width
  398.     GetInterior(&window_interior);
  399.     
  400.     window_rect->left = window_interior.left + upper_left.h;        //  Generate bounding rect
  401.     window_rect->right = window_interior.right + upper_left.h;
  402.     window_rect->top = window_interior.top + upper_left.v;
  403.     window_rect->bottom = window_interior.bottom + upper_left.v;
  404.     
  405. }    //==== CEnhancedWindow::GetRect() ====\\
  406.  
  407.  
  408.  
  409. //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  410. //| CEnhancedWindow::PlaceWithVerify
  411. //|
  412. //| Purpose: Place the window according to bounds.  If this would result in
  413. //|          the window being placed offscreen, move it onscreen.
  414. //|
  415. //| Parameters: bounds: the desired position of the window
  416. //|__________________________________________________________________________
  417.  
  418. void CEnhancedWindow::PlaceWithVerify(Rect *bounds)
  419. {
  420.  
  421.     Rect main_monitor_bounds;
  422.     GDHandle graphics_device;
  423.     graphics_device = GetDeviceList();                            //  Get the first (main) device in the list
  424.     main_monitor_bounds = (*graphics_device)->gdRect;            //  Save the main device bounds rect
  425.     main_monitor_bounds.top += GetMBarHeight();                    //  Shrink screen by menubar size
  426.  
  427.     Rect drag_rect = *bounds;
  428.     drag_rect.bottom = drag_rect.top;
  429.     drag_rect.top -= title_bar_height;                            //  Get the drag rectangle for this window.
  430.  
  431.     Boolean enough_intersection;
  432.     do                                                            //  Scan through all devices
  433.         {
  434.         Rect intersection;
  435.         Rect this_monitor_rect;
  436.         
  437.         this_monitor_rect = (*graphics_device)->gdRect;            //  Get the rect for this monitor
  438.  
  439.         if (graphics_device == GetMainDevice())                    //  Shrink screen by size of menubar
  440.             this_monitor_rect.top += GetMBarHeight();
  441.  
  442.         SectRect (&drag_rect, &this_monitor_rect,                //  If this device intersects the drag rectangle
  443.                     &intersection);                                //   in a 4x4 block, we're okay.
  444.         enough_intersection =     
  445.                 (((intersection.bottom - intersection.top) >= 4) &&
  446.                 ((intersection.right - intersection.left) >= 4));
  447.  
  448.         graphics_device =                                        //  Next device
  449.                 (GDHandle) (*graphics_device)->gdNextGD;
  450.         }
  451.     while (graphics_device && (!enough_intersection));
  452.  
  453.     if (!enough_intersection)                                    //  This window is not draggable from any
  454.                                                                 //  monitor; move it to the main monitor.
  455.         {
  456.         
  457.         const short kSlopFactor = 4;
  458.  
  459.         short window_width = bounds->right - bounds->left;        //  Find size of window
  460.         short window_height = bounds->bottom - bounds->top;
  461.         
  462.         short main_monitor_width = main_monitor_bounds.right -
  463.                                     main_monitor_bounds.left;    //  Find size of main monitor
  464.         short main_monitor_height =
  465.                                 main_monitor_bounds.bottom -
  466.                                     main_monitor_bounds.top;
  467.         
  468.         if (window_width > main_monitor_width)                    
  469.             window_width = main_monitor_width - 2*kSlopFactor;    //  Fit window to monitor, horizontally
  470.         
  471.         if (window_height > main_monitor_height)
  472.             window_height = main_monitor_height - 2*kSlopFactor;//  Fit window to monitor, vertically
  473.  
  474.         if (bounds->left > main_monitor_bounds.right)
  475.             OffsetRect(bounds, -(bounds->left -
  476.                         main_monitor_bounds.right +
  477.                             window_width + kSlopFactor), 0);    //  Nudge barely on screen, to left
  478.         
  479.         if (bounds->right < main_monitor_bounds.left)
  480.             OffsetRect(bounds, main_monitor_bounds.left -
  481.                         bounds->left + kSlopFactor,
  482.                         0);                                        //  Nudge barely on screen, to right
  483.  
  484.         if (bounds->top + kSlopFactor < main_monitor_bounds.top)
  485.             OffsetRect(bounds, 0, main_monitor_bounds.top -
  486.                 bounds->top + title_bar_height + kSlopFactor);    //  Nudge barely on screen, down
  487.  
  488.         if (bounds->top - title_bar_height - kSlopFactor > 
  489.                                 main_monitor_bounds.bottom)
  490.             OffsetRect(bounds, 0, -(bounds->top -
  491.                             main_monitor_bounds.bottom +
  492.                                 window_height + kSlopFactor));    //  Nudge barely on screen, up
  493.  
  494.         }
  495.  
  496.     Place(bounds);                                                //  Place the window
  497.  
  498. }    //==== CEnhancedWindow::PlaceWithVerify() ====\\